﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using devtm.Aop.Emit;
using devtm.Aop.InternalProcess;
using devtm.Aop.Convert;
using Mono.Cecil;
using System.Reflection;
using devtm.Aop.Linq.Boxs;
using devtm.Aop.Event;

namespace devtm.Aop.Linq
{
    
    public class ProxyRuntime<T> : Proxy<T>, IRuntimeSave
    {


        #region Save

        public virtual Type Save()
        {

            #region Create Type

            string[] a = Guid.NewGuid().ToString().Split('-');
            string assemblyName = "Custom" + Type.Name + a.Last();

            string filename = assemblyName + ".dll";

            ModuleCreator moduleCreator = new ModuleCreator
                (
                   assemblyName,
                   filename,
                   new Version(1, 0, 0, 0),
                   Mono.Cecil.ModuleKind.Dll,
                   Type.Assembly.Location
                );

            TypeReference t1 = moduleCreator.Get(Type);
            TypeDefinition typeFinal = moduleCreator.OverrideType(t1.Resolve(), t1.Namespace, t1.Name);

            #endregion


            ImplementJob(moduleCreator, typeFinal);


            foreach (KeyValuePair<string, GroupMethod> item in _list)
            {

                InterceptorContainer box = GetInterceptorContainer(moduleCreator, item.Value.Interceptors);

                foreach (MethodSign m in item.Value.Methods)
                    moduleCreator.OverrideMethod(typeFinal, m, box);

            }


            Assembly assembly = moduleCreator.Save(Script);
            return assembly.GetType(typeFinal.FullName);

        }

        private void ImplementJob(ModuleCreator moduleCreator, TypeDefinition typeFinal)
        {

            ImplementContainer box = new ImplementContainer(moduleCreator.Module);

            foreach (var item in _interfaces)
            {
                for (int i = 0; i < item.Value.TargetMethods.Length; i++)
                {
                    var o1 = item.Value.TargetMethods[i];
                    var o2 = item.Value.InterfaceMethods[i];
                    box.Add(o1, o2);
                }
            }


            if (box.MustBeImplment)
            {

                foreach (var item in _interfaces)
                {
                    typeFinal.Interfaces.Add(typeFinal.Module.Import(item.Value.InterfaceType));
                }

                moduleCreator.CopyFields(box, typeFinal);
                moduleCreator.CopyMethods(box, typeFinal);
            }

        }

        internal InterceptorContainer GetInterceptorContainer(ModuleCreator moduleCreator, Dictionary<Type, IInterceptor> Interceptors)
        {

            var box = new InterceptorContainer();

            foreach (var item in Interceptors)
            {

                var i = item.Value;

                TypeDefinition type;
                int hashcode = 0;
                Mono.Collections.Generic.Collection<Mono.Cecil.CustomAttributeNamedArgument> properties = new Mono.Collections.Generic.Collection<Mono.Cecil.CustomAttributeNamedArgument>();

                if (i is InterceptorTypeBox)
                {

                    type = moduleCreator.Get(i.Type).Resolve();
                    hashcode = type.GetHashCode();

                    foreach (var v in item.Value.Properties)
                    {
                        Mono.Cecil.CustomAttributeNamedArgument arg
                            = new Mono.Cecil.CustomAttributeNamedArgument(v.Key, new CustomAttributeArgument(moduleCreator.Get(v.Value.GetType()), v.Value));
                        properties.Add(arg);
                    }

                }
                else
                {
                    type = null;
                }

                RegisterMethod(moduleCreator, type, hashcode, box, i.BeforeMethod, InterceptEnumEventType.BeforeCall, properties);
                RegisterMethod(moduleCreator, type, hashcode, box, i.CatchMethod, InterceptEnumEventType.Catch, properties);
                RegisterMethod(moduleCreator, type, hashcode, box, i.InterceptMethod, InterceptEnumEventType.InterceptCall, properties);
                RegisterMethod(moduleCreator, type, hashcode, box, i.AfterMethod, InterceptEnumEventType.AfterCall, properties);

            }

            return box;
        }

        private static void RegisterMethod(ModuleCreator moduleCreator, TypeDefinition type, int hashcode, InterceptorContainer box, MethodInfo method, InterceptEnumEventType typeEvent, Mono.Collections.Generic.Collection<Mono.Cecil.CustomAttributeNamedArgument> properties)
        {

            if (method != null)
            {

                var mb = moduleCreator.Get(method).Resolve();

                box.Add(typeEvent, mb, properties, hashcode, type);

            }
        }

        #endregion



        #region CreateInstance

        public object CreateInstance(params object[] args)
        {
            Type t = Save();
            object retour = Activator.CreateInstance(t, args);
            return retour;
        }

        public U CreateInstance<U>(params object[] args)
        {

            Type t = Save();

            U retour = (U)Activator.CreateInstance(t, args);

            return retour;

        }

        #endregion



        public ProxyRuntime(Type type) : base(type)
        {
            
            // reporter les constructeurs
           
        }



   
    }

}
